//ChucK code for Superparticular Samchillian//Samchillian idea by Leon Gruenbaum//superparticular-ratio implementation by Jacob Barton//paste these lines into a new document in miniAudicle, or save text as a .ck to run in command-line//change these to match your input/output device0=>int inDeviceNum;1=>int outDeviceNum;class MicroRobinMidiIO
{
MidiIn min;
MidiOut mouse;
MidiMsg inmsg, outmsg;0=>int ctr;//int rr[128][3]; don't think we need this now.// index = note number?// column 0 = channel sent to// column 1 = note number sentint chans[14];// list of channels usedfloat holds[16];// pitches of on notes, zero if off.[0,1,2,3,4,5,6,7,8,10,11,12,13,14] @=> chans;//exclude channel 10 (drums) & 16 (send channel)//microtuning stuff// PitchBend// input: pitch (midi note number float) & velocity of desired note// action: sends appropriate pitchbend message// (assuming pitchbend range = +/- 2 semitones)// output: note number required for correct frequency to be realized// sends pitchbend, assuming +/- wholestep pitchbend range// returns note number required for correct frequency
fun int PitchBend(float pitch, int velocity){//send pitchbend224+ chans[ctr]=> outmsg.data1;0=> outmsg.data2;Math.round((pitch %1.0)*32.0+64.0) $ int=> outmsg.data3;
mouse.send(outmsg);returnMath.floor(pitch) $ int;}// StartRelay// input: number of MIDI device, MidiTransform to be used// creates a loop ~ should be sporked
fun void StartRelay(int deviceNum, MidiTransform mt){if(!min.open(inDeviceNum)) me.exit();if(!mouse.open(outDeviceNum)) me.exit();// print out device that was opened<<< min.num(), " -> ", min.name()>>>;<<< mouse.num(), " -> ", mouse.name()>>>;while(true){
min => now;while( min.recv(inmsg)){if( inmsg.data1%16==0)// only receive on channel 1{<<<"r ", inmsg.data1/16, inmsg.data1%16, inmsg.data2, inmsg.data3>>>;if( inmsg.data1/16==9){
mt.NoteOn(inmsg.data2, inmsg.data3);}if( inmsg.data1/16==8){
mt.NoteOff(inmsg.data2, inmsg.data3);}if( inmsg.data1/16==12){//prog change apply to channels 1-16//works!
inmsg.data1-(inmsg.data1%16)=>int base;for(0=>int i; i<15; i++){
base + chans[i]=> outmsg.data1;
inmsg.data2=> outmsg.data2;
i++;if( i ==15){0=> outmsg.data3;
mouse.send(outmsg);break;}
base + chans[i]=> outmsg.data3;
mouse.send(outmsg);
inmsg.data2=> outmsg.data1;
i++;if( i ==15){0=> outmsg.data2=> outmsg.data3;
mouse.send(outmsg);break;}
base + chans[i]=> outmsg.data2;
inmsg.data2=> outmsg.data3;
mouse.send(outmsg);}}if( inmsg.data1/16==11){//apply any controller data to channels 1-16
inmsg.data1-(inmsg.data1%16)=>int base;
inmsg.data2=> outmsg.data2;
inmsg.data3=> outmsg.data3;for(0=>int i; i<16; i++){
base + i => outmsg.data1;
mouse.send(outmsg);}}}}}}// NoteOn// input: pitch in midi note-number extended, velocity// action: sends a MIDI pitchbend + note-on message to mouse on the current channel// keeping track of holds
fun void NoteOn(float nn, int velocity){
IncrementCtr();
nn => holds[chans[ctr%14]];
PitchBend(nn, velocity)=> outmsg.data2;// note on, right channel144+ chans[ctr%14]=> outmsg.data1;
velocity => outmsg.data3;
mouse.send(outmsg);//<<< "s ", outmsg.data1 / 16, outmsg.data1 % 16, outmsg.data2, outmsg.data3>>>;}// increments mod-14 counter, skipping over channels with// notes already on them, if possible.
fun void IncrementCtr(){
ctr;0=>int i;for( i; i<14; i++){if( holds[chans[(ctr+i)%14]]==0.0){(ctr + i)%14=> ctr;return;}}(ctr +1)%14=> ctr;}// NoteOff// input: pitch & note-off velocity// action: finds the pitch & offs it.
fun void NoteOff(float nn, int velocity){0=>int c;for(c; c<16; c++){if(holds[c]== nn)// we found the pitch!{128+ c => outmsg.data1;Math.floor(nn) $ int=> outmsg.data2;
velocity => outmsg.data3;
mouse.send(outmsg);//<<< "s ", outmsg.data1 / 16, outmsg.data1 % 16, outmsg.data2, outmsg.data3>>>;0.0=> holds[c];return;}}<<<"MISS", nn>>>;// we couldn't find the pitch!// don't do anything. }
fun void ControlChange(int channel, int prognum, int val){128+ channel => outmsg.data1;
prognum => outmsg.data2;
val => outmsg.data3;}}class MidiTransform
{// superclass for MIDI transformers to be used by MicroRobinMidiIO
MicroRobinMidiIO myIO;
fun void LinkToIO(MicroRobinMidiIO io){
io @=> myIO;}
fun void NoteOn(int nn, int vel){return;}
fun void NoteOff(int nn, int vel){return;}}class SuperparticularSamchillian extends MidiTransform
{57. =>float resentFreq;57. =>float prevFreq;62=>int keyboardCenterNN;float prevFreqsByKey[128];//set pans funky//for(0=>int f; f<16; f++)//{// myIO.ControlChange(f, 9, f*8);//}
fun void NoteOn(int nn, int vel){if(nn == keyboardCenterNN){
myIO.NoteOn( Std.ftom(prevFreq), vel);}else{if(nn < keyboardCenterNN){// superparticular!1. +1./(keyboardCenterNN - nn)/=> prevFreq;
myIO.NoteOn( Std.ftom(prevFreq), vel);}else{if(nn > keyboardCenterNN){1. +1./(nn - keyboardCenterNN)*=> prevFreq;
myIO.NoteOn( Std.ftom(prevFreq), vel);}}}
Std.ftom(prevFreq)=> prevFreqsByKey[nn];}
fun void NoteOff(int nn, int vel){
myIO.NoteOff( prevFreqsByKey[nn], vel);}}
MicroRobinMidiIO mrmio;
SuperparticularSamchillian easy;
easy.LinkToIO(mrmio);
spork ~ mrmio.StartRelay(1, easy);1::second => now;
KBHit kb;while(true){
kb => now;while( kb.more()){
kb.getchar()=>int c;<<<"ascii:", c>>>;
easy.NoteOn(c, 88);}}